package com.yami.shop.common.log;


import cn.hutool.core.util.NumberUtil;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.Json;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

@Slf4j
@Aspect
@Component
public class MonitorAspect {

    private static final ThreadLocal<LocalDateTime> TIME_THREAD_LOCAL = new ThreadLocal<>();

    /**
     * 定义切点
     */
    @Pointcut("@annotation(io.swagger.annotations.ApiOperation) " +
            "|| @annotation(org.springframework.web.bind.annotation.GetMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.PostMapping)" +
            "" +
            "")
    public void pointCut() {

    }

    /**
     * 方法前置处理 有无异常都执行
     */
    @Before("pointCut()")
    public void before() {
        TIME_THREAD_LOCAL.set(LocalDateTime.now());
    }

    /**
     * 方法后置处理 无论是否异常都执行
     */
    /*
     * @After("pointCut()") public void after(){
     *
     * }
     */

    /**
     * 方法正常退出处理 无异常退出-执行
     */
    @AfterReturning(returning = "rvt", pointcut = "pointCut()")
    public void afterReturning(JoinPoint point, Object rvt) {
        getMonitorData(point, rvt, false);
    }

    /**
     * 方法异常中断处理 出现异常-执行 传播异常行为
     */
    @AfterThrowing(pointcut = "pointCut()", throwing = "a")
    public void afterThrowing(JoinPoint point, Throwable a) {
        getMonitorData(point, a, true);
    }

    /**
     * 组装监控数据
     */
    public void getMonitorData(JoinPoint point, Object result, boolean isException) {

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        String method = request.getMethod();
        String url = request.getRequestURL().toString();
        String requestParam = "";
        String responseParam = "";

        // 请求信息
        if (HttpMethod.POST.name().equals(method)) {
            try {
                Object[] args = point.getArgs();
                if (EmptyUtils.isNotEmpty(args)) {
                    for (Object arg : args) {
                        if (!(arg instanceof org.springframework.validation.BeanPropertyBindingResult
                                )) {

                            if (arg instanceof MultipartFile) {
                                MultipartFile file = (MultipartFile) arg;
                                double kiloByte = NumberUtil.div(file.getSize(), 1024, 2);
                                String fileMsg = "{fileName:" + file.getOriginalFilename()+ "}," + "{fileSize-" + kiloByte+"kb}";
                                requestParam = requestParam + fileMsg;
                            }
                            /*else if(arg instanceof WebStatFilter.StatHttpServletResponseWrapper){
                                requestParam = requestParam + "文件下载，输出流状态：" +  ((WebStatFilter.StatHttpServletResponseWrapper) arg).getStatus();
                            }*/
                            else {
                                requestParam = requestParam + Json.toJsonString(arg);
                            }

                        }
                    }
                }
            }catch (Exception e){
                log.error("",e);
            }

        }

        // get和post都可能把url作为传参
        requestParam = requestParam + (request.getQueryString());

        // 返回信息
        if (isException) {
            // 异常生成日志的traceId
            if (result instanceof YamiShopBindException) {
                YamiShopBindException commonException = (YamiShopBindException) result;
//                responseParam = JSON.toJSONString(new AjaxResult(commonException.getHttpStatusCode(), commonException.getMessage()));
            } else {
                Throwable throwable = (Throwable) result;
//                responseParam = JSON.toJSONString(new AjaxResult(-1,throwable.getMessage()));
            }
            Throwable throwable = (Throwable) result;
            //log.error(throwable.getMessage(),throwable);
            log.error("message:", throwable);
        } else {
            if (EmptyUtils.isNotEmpty(result)) {
                responseParam = Json.toJsonString(result);
            }
        }

        // 获取线程本地变量
        String startTimeStr = "";
        LocalDateTime startTime = TIME_THREAD_LOCAL.get();
        if (EmptyUtils.isNotEmpty(startTime)) {
            startTimeStr = startTime.toString().replace("T", " ");
        }

        // 异常这边日志信息中输出traceId
        if (isException) {
            log.error(
                    "[请求日志]-{\"url\":\"{}\",\"method\":\"{}\",\"request\":{},\"response\":{},\"startTime\":\"{}\",\"endTime\":\"{}\"}"
                    ,url, method, requestParam, responseParam, startTimeStr,
                    LocalDateTime.now().toString().replace("T", " "));
        } else {
            log.info(
                    "[请求日志]-{\"url\":\"{}\",\"method\":\"{}\",\"request\":{},\"response\":{},\"startTime\":\"{}\",\"endTime\":\"{}\"}",
                    url, method, requestParam, responseParam, startTimeStr,
                    LocalDateTime.now().toString().replace("T", " "));
        }

        TIME_THREAD_LOCAL.remove();
    }
}